#!/bin/sh
# Copyright 2015 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# This script provides easy access to data contained in cros-regions.json (the
# region database for Chromium OS).

# The CROS_VPD_CACHE is defined in dump_vpd_log "filtered_file".
: ${CROS_VPD_CACHE:=/mnt/stateful_partition/unencrypted/cache/vpd/filtered.txt}
: ${CROS_REGIONS_DATABASE=/usr/share/misc/cros-regions.json}

load_vpd_cache() {
  # There are multiple ways to read from VPD. The best approach is probably
  # to call dump_vpd_log directly, but this script may be executed in
  # boot-time (before stateful partition was mounted) or inside initramfs
  # so we cannot use that. Instead we have to try the cache and then read via
  # command 'vpd'.

  if [ ! -f "${CROS_VPD_CACHE}" ]; then
    CROS_VPD_CACHE="$(mktemp)"
    trap "rm ${CROS_VPD_CACHE}" EXIT
    vpd -i RO_VPD -l >"${CROS_VPD_CACHE}"
  fi
}

get_data_from_vpd() {
  local name="$1"
  sed -nr 's/^"'"${name}"'"="(.*)"$/\1/p' "${CROS_VPD_CACHE}"
}

get_legacy_value_from_vpd() {
  local data_name="$1"
  local select_first="$2"
  local name value entry

  # There are some legacy VPD values that should override region data.
  local legacy_map="
    locales=initial_locale
    time_zones=initial_timezone
    keyboards=keyboard_layout"

  for entry in ${legacy_map}; do
    name="${entry%=*}"
    if [ "${name}" != "${data_name}" ]; then
      continue
    fi

    name="${entry#*=}"
    value="$(get_data_from_vpd "${name}")"

    if [ -n "${value}" ]; then
      # Legacy values were defined in CSV so we have to convert output.
      if ${select_first}; then
        echo "${value%%,*}"
      else
        echo "${value}" | tr ',' '\n'
      fi
      return 0
    fi
  done
  return 1
}

lookup_data() {
  local name="$1"
  local region="$2"
  local select_first="$3"
  local output=""
  local pattern='.[$region][$name]'

  # Check value type and modify pattern for array.
  output="$(jq -c -r --arg region "${region}" --arg name "${name}" \
            "${pattern}|arrays" "${CROS_REGIONS_DATABASE}")"
  if [ -n "${output}" ]; then
    if ${select_first}; then
      pattern="${pattern}[0]"
    else
      pattern="${pattern}[]"
    fi
  fi

  output="$(jq -c -r --arg region "${region}" --arg name "${name}" \
            "${pattern}" "${CROS_REGIONS_DATABASE}")"
  if [ "${output}" != "null" ]; then
    echo "${output}"
  fi
}

# Use legacy VPD data to look up region code.
reverse_lookup_region_by_timezone() {
  local value="$1"
  local region="$(jq -c -r --arg "timezone" "${value}" \
    'map(select(.time_zones == [$timezone]).region_code)[0]' \
    "${CROS_REGIONS_DATABASE}")"
  if [ "${region}" != "null" ]; then
    echo "${region}"
  fi
}

report_available_fields() {
  if [ ! -f "${CROS_REGIONS_DATABASE}" ]; then
    return
  fi
  echo "Availalbe fields in current system:"
  # Region 'us' should be always available.
  jq -c -r ".us|keys[]" "${CROS_REGIONS_DATABASE}" | sed 's/^/\t/'
}

usage() {
  echo "Usage: $1 [-s] data_name [region]

        -s:        Selects only the first element (if value is list).
        data_name: Name of field inside ${CROS_REGIONS_DATABASE}.
        region:    Region code to query. Read from VPD if omitted.
        " >&2
  report_available_fields >&2
}

main() {
  local region="" data_name=""
  local value=""
  local select_first=false
  local myname="$0"

  if [ "$1" = "-s" ]; then
    select_first=true
    shift
  fi

  case "$#" in
    1)
      data_name="$1"
      ;;
    2)
      data_name="$1"
      region="$2"
      ;;
    *)
      usage "${myname}"
      exit 1
      ;;
  esac

  if [ -z "${region}" ]; then
    load_vpd_cache
    region="$(get_data_from_vpd region)"
    if get_legacy_value_from_vpd "${data_name}" ${select_first} ; then
      # Already printed.
      exit 0
    fi
    if [ -z "${region}" ]; then
      region="$(reverse_lookup_region_by_timezone \
        "$(get_data_from_vpd initial_timezone)")"
    fi
  fi

  lookup_data "${data_name}" "${region}" ${select_first}
}

set -e
main "$@"
